precision highp float;

varying vec2 var_pos;
uniform float time;
uniform float anim_time;
uniform int num_samples;
uniform float shutter;
const int MAX_NUM_SAMPLES = 256;

// uniform over [0, 1]
float rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); }
float uniform_dist(vec2 co, int n) { return rand(co + 0.07 * time + 0.11 * float(n)); }


float circle(vec2 pos, float radius)
{
    return float(length(pos) < radius);
}

vec2 orbit(vec2 pos, float radius, float t)
{
    return pos + vec2(cos(t), sin(t)) * radius;
}

vec4 scene(vec2 pos, float t)
{
    vec2   big_orbit = orbit(        pos, 0.5, t *  3.0 + sin(t*5.0));
    vec2 small_orbit = orbit(  big_orbit, 0.3, t * 10.0);
    vec2  tiny_orbit = orbit(small_orbit, 0.1, t * 17.0);

    vec4 tiny     = circle( tiny_orbit, 0.02) * vec4(0.3, 0.8, 0.1, 1);
    vec4 satelite = circle(small_orbit, 0.05) * vec4(0.7, 0.2, 0.1, 1);
    vec4 core     = circle(  big_orbit, 0.08) * vec4(0.8, 0.4, 0.2, 1);

    vec4 color = core + satelite + tiny;
    color.a = min(1.0, color.a);
    color.rgb *= color.a;       // pre-multiplied alpha
    return color;
}


void main()
{
    vec4 color = vec4(0);
    int nsamples = num_samples;

    for (int i=0; i<MAX_NUM_SAMPLES; i++) {
        if (i == nsamples) break;

        float bias = (float(i) + uniform_dist(var_pos, i)) / float(nsamples) * shutter;
        color += scene(var_pos, anim_time - bias);
    }

    color /= float(nsamples);
    color.rgb /= color.a + 1e-5;   // undo pre-multiplied alpha

    color = mix(vec4(0, 0, 0, 1), color, color.a);  // add background
    color = pow(color, vec4(1.0 / 2.2));            // convert from linear space to gamma corrected space
    gl_FragColor = color;
}

